package com.javaboy.vhr.controller;

import com.github.pagehelper.PageInfo;
import com.javaboy.vhr.entity.FileInfo;
import com.javaboy.vhr.enums.FileUseEnum;
import com.javaboy.vhr.service.FileInfoService;
import com.javaboy.vhr.utils.Base64ToMultipartFile;
import com.javaboy.vhr.utils.result.ResultDTO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.*;

/**
 * @author: gaoyang
 * @date: 2021-03-23 10:46:33
 * @description: 文件(FileInfo)表控制层
 */
@Slf4j
@Api(tags = "文件API")
@RestController
@RequestMapping("/file")
public class FileInfoController {

    @Value("${localUploadFilePath}")
    private String FILE_PATH;

    @Resource
    private FileInfoService fileInfoService;

    @ApiOperation(value = "文件上传")
    @PostMapping("/upload")
    public ResultDTO<FileInfo> upload(@RequestBody FileInfo fileInfo) throws InterruptedException {
        String use = fileInfo.getFileUse();
        String key = fileInfo.getFileKey();
        String suffix = fileInfo.getSuffix();
        String shardBase64 = fileInfo.getShard();
        MultipartFile shard = Base64ToMultipartFile.base64ToMultipart(shardBase64);

        // 保存文件到本地
        FileUseEnum useEnum = FileUseEnum.getByCode(use);
        // 如果目录不存在则创建
        String dir = useEnum.name().toLowerCase();
        File fullDir = new File(FILE_PATH + dir);
        if (!fullDir.exists()) {
            fullDir.mkdirs();
        }

        // course\6sfSqfOwzmik4A4icMYuUe.mp4
        String path = new StringBuffer(dir)
                .append(File.separator)
                .append(key)
                .append(".")
                .append(suffix)
                .toString();
        // course\6sfSqfOwzmik4A4icMYuUe.mp4.1
        String localPath = new StringBuffer(path)
                .append(".")
                .append(fileInfo.getShardIndex())
                .toString();
        String fullPath = FILE_PATH + localPath;
        File dest = new File(fullPath);
        try {
            // 保存文件
            shard.transferTo(dest);
        } catch (IOException e) {
            log.error(e.getMessage());
            return ResultDTO.error("上传失败-" + e.getMessage(), null);
        }

        // 保存文件记录
        fileInfo.setFilePath(path);
        FileInfo model = this.fileInfoService.queryByKey(fileInfo.getFileKey());
        if (model == null) {
            this.fileInfoService.insert(fileInfo);
        } else {
            model.setShardIndex(fileInfo.getShardIndex());
            this.fileInfoService.update(model);
        }
        if (fileInfo.getShardIndex().equals(fileInfo.getShardTotal())) {
            this.merge(fileInfo);
        }
        return ResultDTO.success("上传成功", fileInfo);
    }

    /**
     * 文件合并
     */
    public void merge(FileInfo fileInfo) throws InterruptedException {
        log.info("合并分片开始");
        // course\6sfSqfOwzmik4A4icMYuUe.mp4
        String path = fileInfo.getFilePath();
        Integer shardTotal = fileInfo.getShardTotal();
        File newFile = new File(FILE_PATH, path);
        // 文件追加写入
        FileOutputStream outputStream = null;
        try {
            outputStream = new FileOutputStream(newFile, true);
        } catch (FileNotFoundException e) {
            log.error(e.getMessage());
        }
        // 分片文件
        FileInputStream fileInputStream = null;
        byte[] bytes = new byte[10 * 1024 * 1024];
        int len;

        try {
            for (Integer i = 0; i < shardTotal; i++) {
                // 读取第 i 个分片
                fileInputStream = new FileInputStream(new File(FILE_PATH + path + "." + (i + 1)));
                while ((len = fileInputStream.read(bytes)) != -1) {
                    outputStream.write(bytes, 0, len);
                }
            }

        } catch (IOException e) {
            log.error("合并分片异常-" + e.getMessage());
        } finally {
            try {
                if (fileInputStream != null) {
                    fileInputStream.close();
                }
                outputStream.close();
                log.info("IO流关闭");
            } catch (IOException e) {
                log.error("IO流关闭失败-", e.getMessage());
            }
        }
        log.info("合并分片结束");

        // 释放虚拟机对文件的占用
        System.gc();
        Thread.sleep(100);

        log.info("删除分片开始");
        for (Integer i = 0; i < shardTotal; i++) {
            String filePath = FILE_PATH + path + "." + (i + 1);
            File file = new File(filePath);
            boolean result = file.delete();
            log.info("删除{},{}", filePath, result ? "成功" : "失败");
        }
        log.info("删除分片结束");
    }

    @ApiOperation(value = "文件上传检查")
    @GetMapping("/check")
    public ResultDTO<FileInfo> check(@RequestParam(name = "fileKey") String fileKey) {
        FileInfo fileInfo = this.fileInfoService.queryByKey(fileKey);
        return ResultDTO.success(fileInfo);
    }

    @ApiOperation(value = "分页查询")
    @GetMapping("/list")
    public ResultDTO<PageInfo<FileInfo>> list(FileInfo fileInfo,
                                              @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
                                              @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {
        PageInfo<FileInfo> pageList = this.fileInfoService.pageList(fileInfo, pageNo, pageSize);
        return ResultDTO.success(pageList);
    }

    @ApiOperation(value = "新增")
    @PostMapping("/add")
    public ResultDTO<FileInfo> add(@RequestBody FileInfo fileInfo) {
        FileInfo model = this.fileInfoService.insert(fileInfo);
        return ResultDTO.success(model);
    }

    @ApiOperation(value = "修改")
    @PutMapping("/edit")
    public ResultDTO<FileInfo> edit(@RequestBody FileInfo fileInfo) {
        FileInfo model = this.fileInfoService.update(fileInfo);
        return ResultDTO.success(model);
    }

    @ApiOperation(value = "删除")
    @DeleteMapping("/delete/{id}")
    public ResultDTO<Void> delete(@PathVariable String id) {
        this.fileInfoService.deleteById(id);
        return ResultDTO.success("删除成功");
    }

}